[LMCache] Add LMCache arm on tuned DSV4 FP4 B300 vLLM AgentX recipe / 在调优后的 DSV4 FP4 B300 vLLM AgentX 配方上新增 LMCache 分支#2232
[LMCache] Add LMCache arm on tuned DSV4 FP4 B300 vLLM AgentX recipe / 在调优后的 DSV4 FP4 B300 vLLM AgentX 配方上新增 LMCache 分支#2232ApostaC wants to merge 2 commits into
Conversation
Combine the tuned B300 recipe from PR #2225 (nightly image, sparse DSV4 FlashInfer attention, FULL_DECODE_ONLY CUDA graphs, AMXF4 mega-MoE) with the LMCache 0.5.1 KV-offload backend from PR #2153, mirroring the B200 arm in PR #2231. The lmcache arm keeps the stock PyTorch caching allocator instead of PYTORCH_ALLOC_CONF=expandable_segments:True (expandable-segment cuMem/VMM allocations cannot be CUDA-IPC-exported to the LMCache MP server, the same failure mode as --enable-cumem-allocator on B200) and runs otherwise identical serving flags, so backends are directly comparable. Adds a standalone dsv4-fp4-b300-vllm-agentic-lmcache config section mirroring the vllm-simple DEP4/DEP8 concurrency ladders and a perf-changelog entry triggering it. 中文:将 PR #2225 的 B300 调优配方(nightly 镜像、稀疏 DSV4 FlashInfer 注意力、FULL_DECODE_ONLY CUDA graph、AMXF4 mega-MoE)与 PR #2153 的 LMCache 0.5.1 KV 卸载后端合并,对应 B200 的 PR #2231。lmcache 分支仅以 默认 PyTorch 缓存分配器替代 PYTORCH_ALLOC_CONF=expandable_segments:True (expandable segments 的 cuMem/VMM 分配无法通过 CUDA IPC 导出给 LMCache MP server,与 B200 上 --enable-cumem-allocator 的失败模式相同),其余 serving 参数与其他分支保持一致,便于卸载后端间直接对比。新增独立的 dsv4-fp4-b300-vllm-agentic-lmcache 配置(测试点与 vllm-simple 的 DEP4/DEP8 阶梯对齐)及触发它的 perf-changelog 条目。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Thanks for the contribution! Please reach out to respective companies' CODEOWNER to fill in the latest PR_REVIEW_CHECKLIST.md before pinging core maintainer on Slack for review. In order for the signoff PR check bot to trigger, you must follow the PR_REVIEW_CHECKLIST.md template correctly, including the phrase For PR verification, add the PR authors are responsible for ensuring that after merging, all GitHub Action jobs fully pass. A lot of the time, failures are just flakes and simply re-running the failed jobs will fix it. See GitHub's docs on re-running failed jobs 感谢你的贡献!请联系相应公司的 CODEOWNER 填写最新的 PR_REVIEW_CHECKLIST.md,然后再在 Slack 上联系核心维护者进行审阅。为了触发 signoff PR 检查机器人,你必须正确遵循 PR_REVIEW_CHECKLIST.md 模板,包括保留英文语句 如需进行 PR 验证,请为此 PR 添加 PR 作者有责任确保合并后所有 GitHub Action 任务完全通过。 很多时候失败只是偶发抖动(flake),重新运行失败的任务即可解决。参见 GitHub 关于重新运行失败任务的文档 |
中文:将 perf-changelog 条目的 pr-link 填写为 PR #2232。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| - "LMCache MP server (lmcache_driven transfer mode) + LMCacheMPConnector per PR #2153/#2231; L1 pool derated to 75% of TOTAL_CPU_DRAM_GB; PYTORCH_ALLOC_CONF=expandable_segments:True dropped on the lmcache arm only (VMM allocations cannot be CUDA-IPC-exported to the LMCache server, same failure mode as cuMem on B200)" | ||
| - "Points mirror the vllm-simple ladders: TP4-DEP4 conc [8, 16, 24, 32, 40, 48, 56, 64, 72]; TP8-DEP8 conc [32, 64, 96, 128, 160, 192, 224, 228]" | ||
| - "Parent dsv4-fp4-b300-vllm-agentic updated in the same change to the PR #2225 recipe and search space (image bump, vllm-simple + Mooncake arms) so the shared script stays consistent; the parent points are benchmarked by PR #2225 and not re-triggered here" | ||
| pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/XXXX |
There was a problem hiding this comment.
🔴 The new perf-changelog.yaml entry's pr-link uses pull/XXXX (four X's), but validate_perf_changelog.py's PR_LINK_PLACEHOLDERS only recognizes XXX (three X's) or the real canonical PR link. This mismatch means the placeholder will never get canonicalized to the real PR link during the merge-with-reuse flow. Fix by changing it to pull/XXX (three X's) or the real PR number (2232).
Extended reasoning...
The appended entry in perf-changelog.yaml (line 4827) sets:
pr-link: https://github.com/SemiAnalysisAI/InferenceX/pull/XXXXfour X characters. utils/validate_perf_changelog.py defines:
CANONICAL_PR_LINK = re.compile(r"https://github.com/SemiAnalysisAI/InferenceX/pull/\d+")
PR_LINK_PLACEHOLDERS = {
"XXX",
"https://github.com/SemiAnalysisAI/InferenceX/pull/XXX",
}Both of these expect exactly three X's, not four. pull/XXXX therefore matches neither the canonical digit-based link pattern nor the placeholder set.
Step-by-step proof:
validate_added_pr_link()is invoked withlink = "https://github.com/SemiAnalysisAI/InferenceX/pull/XXXX".- If
pr_numberisNone(e.g., a main-branch validation run), the code requiresCANONICAL_PR_LINK.fullmatch(link). The regex requires\d+afterpull/;XXXXis not digits, sofullmatchreturnsNoneand the check fails. - If
pr_numberis set (a PR-run, e.g. 2232), the code computesexpected = ".../pull/2232"and checkslink not in PR_LINK_PLACEHOLDERS and link != expected."https://github.com/SemiAnalysisAI/InferenceX/pull/XXXX"is not inPR_LINK_PLACEHOLDERS(which only contains the three-X forms) and is not equal toexpected, so the condition is true andChangelogValidationError("new PR entry must use ... or an XXX placeholder; found ...")is raised. - Either way, the placeholder as written is rejected.
This check is reached at merge time via prepare_perf_changelog_merge.py's canonicalize/resolve-conflict subcommands, which utils/merge_with_reuse.sh invokes with the real --pr-number to rewrite placeholder links to the real PR URL. Because compare_entries() → validate_added_pr_link() rejects pull/XXXX outright, that canonicalization step will fail, meaning the reuse-merge tooling cannot resolve this entry to the real PR link and the merge flow breaks for this changelog entry. (Separately, the PR-time run-sweep.yml CI invocation of validate_perf_changelog.py only runs validate_generated_config(), which does not call this comparison path, so it will not itself fail at PR-review time — but the PR description's claim that "validate_perf_changelog.py against origin/main passes" is not evidence this placeholder is valid, since that path isn't exercised by the local check either.)
The fix is a one-character edit: change pull/XXXX to pull/XXX (three X's) to match the accepted placeholder, or simply write the real PR number 2232 directly.
| unset VLLM_USE_SIMPLE_KV_OFFLOAD | ||
| OFFLOAD_ARGS=( | ||
| --kv-transfer-config | ||
| '{"kv_connector":"MooncakeStoreConnector","kv_role":"kv_both","kv_connector_extra_config":{"load_async":true}}' | ||
| "{\"kv_connector\":\"LMCacheMPConnector\",\"kv_role\":\"kv_both\",\"kv_connector_extra_config\":{\"lmcache.mp.host\":\"$LMCACHE_CONNECT_HOST\",\"lmcache.mp.port\":$LMCACHE_PORT,\"lmcache.mp.mq_timeout\":$LMCACHE_MQ_TIMEOUT}}" | ||
| ) | ||
| fi | ||
| ;; |
There was a problem hiding this comment.
🔴 The new LMCache arm's --kv-transfer-config sets kv_connector":"LMCacheMPConnector" but omits kv_connector_module_path, unlike the two sibling scripts (dsv4_fp4_mi355x_vllm.sh:344, kimik2.5_fp4_b200.sh:150) that wire up the same connector. Since LMCacheMPConnector is out-of-tree, vLLM will not be able to locate the class and vllm serve should fail at KV-connector construction, breaking every point in the new dsv4-fp4-b300-vllm-agentic-lmcache config. Fix by adding "kv_connector_module_path":"lmcache.integration.vllm.lmcache_mp_connector" to the config dict.
Extended reasoning...
The bug: In the lmcache) case of benchmarks/single_node/agentic/dsv4_fp4_b300_vllm.sh (around lines 254-259), OFFLOAD_ARGS is built as:
\n"{\"kv_connector\":\"LMCacheMPConnector\",\"kv_role\":\"kv_both\",\"kv_connector_extra_config\":{...}}"\n
This JSON blob is missing the kv_connector_module_path key.
Why it matters: LMCacheMPConnector is not a vLLM built-in connector (unlike MooncakeStoreConnector, which vLLM resolves through its internal connector-factory registry). It lives in the external lmcache package, at lmcache.integration.vllm.lmcache_mp_connector. vLLM's KVTransferConfig resolves out-of-tree connectors by dynamically importing the module named in kv_connector_module_path before looking up the connector class by name — without that field, vLLM has no way to find the class.
Confirmed by direct comparison with sibling scripts: I checked the two other scripts in this repo that wire up the identical LMCacheMPConnector:
benchmarks/single_node/agentic/dsv4_fp4_mi355x_vllm.sh:344:"kv_connector\":\"LMCacheMPConnector\",\"kv_connector_module_path\":\"lmcache.integration.vllm.lmcache_mp_connector\"...benchmarks/single_node/agentic/kimik2.5_fp4_b200.sh:150(also lmcache 0.5.1, matching this PR's version exactly): same pattern.
Both sibling scripts explicitly set kv_connector_module_path alongside kv_connector. This new B300 arm is the only one of the three that omits it.
Why nothing else catches this: The script does run python3 -c \"import lmcache.integration.vllm.lmcache_mp_connector\" earlier, but that only verifies the module is importable in the launcher process — it says nothing to vLLM about where to import the connector class from when it parses --kv-transfer-config. bash -n, the matrix-logic pytest suite, and generate_sweep_configs.py all validate shell syntax and config-generation logic, not the semantics of the JSON payload passed to vLLM, so none of them would catch this.
Step-by-step proof of the failure:
KV_OFFLOAD_BACKEND=lmcacheis selected, LMCache MP server starts and passes its healthcheck.OFFLOAD_ARGSis set to--kv-transfer-config '{\"kv_connector\":\"LMCacheMPConnector\",\"kv_role\":\"kv_both\",\"kv_connector_extra_config\":{...}}'(nokv_connector_module_path).vllm serveparses this into aKVTransferConfig. Sincekv_connector_module_pathis absent, vLLM falls back to its built-in connector-factory lookup by name.LMCacheMPConnectoris not registered there (it is only importable via the externallmcachepackage), so the lookup fails and vLLM raises an error during KV-connector construction, before the server can start serving.- Every concurrency point in the new
dsv4-fp4-b300-vllm-agentic-lmcachesearch space (17 points total) uses this same code path, so the entire new config fails at server startup, not just some points.
Fix: add \"kv_connector_module_path\":\"lmcache.integration.vllm.lmcache_mp_connector\" to the JSON dict at the same spot the two sibling scripts do, e.g.:
\n"{\"kv_connector\":\"LMCacheMPConnector\",\"kv_connector_module_path\":\"lmcache.integration.vllm.lmcache_mp_connector\",\"kv_role\":\"kv_both\",\"kv_connector_extra_config\":{...}}"\n
Severity is normal, not a nit or pre-existing issue: this is new code introduced by this PR, and merging as-is causes a concrete, deterministic startup failure for the entire new lmcache arm — not a stylistic or description mismatch.
| @@ -1,60 +1,47 @@ | |||
| #!/usr/bin/env bash | |||
| set -euo pipefail | |||
| set -eo pipefail | |||
There was a problem hiding this comment.
🟡 Line 2 weakens set -euo pipefail to set -eo pipefail, dropping nounset — this is the only script among 23 in benchmarks/single_node/agentic/ that does so (its B200 sibling dsv4_fp4_b200_vllm.sh from #2231 handles the exact same optionally-unset vars with ${VAR:-} instead). Recommend restoring set -u and using ${KV_OFFLOAD_BACKEND:-}, ${SLURM_JOB_ID:-}, and ${MODEL_PATH:-} in the new unguarded expansions instead of disabling nounset for the whole ~350-line script.
Extended reasoning...
What changed: Line 2 of dsv4_fp4_b300_vllm.sh changes set -euo pipefail to set -eo pipefail, dropping the -u (nounset) flag. I verified this is the only script among all 23 scripts in benchmarks/single_node/agentic/ that lacks nounset — every other script, including the direct B200 counterpart dsv4_fp4_b200_vllm.sh introduced in #2231 (which this PR explicitly says it mirrors), keeps set -euo pipefail.
Why it was dropped: The rewrite replaces the old declare -p VAR >/dev/null 2>&1 && [ -n "$VAR" ] guards for MODEL_PATH/SLURM_JOB_ID with plain [[ -n "$VAR" ]], and adds a new case "$KV_OFFLOAD_BACKEND" in "") branch that relies on KV_OFFLOAD_BACKEND expanding to empty when unset. None of KV_OFFLOAD_BACKEND, SLURM_JOB_ID, or MODEL_PATH are in the check_env_vars allowlist, so under nounset these bare references would abort with "unbound variable" whenever the var isn't exported (e.g. a stand-alone run without SLURM, or the vllm-simple/mooncake KV_OFFLOAD_BACKEND path). Dropping -u script-wide is the blunt fix for that.
Why this is unnecessary: The B200 sibling script that this PR is explicitly mirroring solves the identical problem without sacrificing nounset, using the standard ${VAR:-} parameter-expansion idiom: [[ -n "${SLURM_JOB_ID:-}" ]], ${SLURMD_NODENAME:-unknown}, [[ -n "${MODEL_PATH:-}" ]]. benchmark_lib.sh itself uses this idiom throughout. So the intent (let three specific optional vars expand to empty) can be fully achieved without disabling the safety net for the other ~350 lines of the script — the fix conflated "these three vars are allowed to be unset" with "disable unbound-variable checking everywhere."
Impact: With nounset off, any future typo in a variable reference anywhere in this script (e.g. a misspelled $LMCACHE_PORT, $TOTAL_CPU_DRAM_GB, or $GPU_COUNT) will silently expand to an empty string instead of aborting immediately with "unbound variable." Concretely: GPU_COUNT=$TP further down is guarded by a regex check so a typo there would still be caught, but a hypothetical typo inside one of the arithmetic expressions (e.g. LMCACHE_L1_SIZE_GB=$((TOTAL_CPU_DRAM_GB * 3 / 4)) misspelled as TOTAL_CPU_DRM_GB) would silently evaluate to 0 rather than failing fast, producing a confusing downstream failure (or a nonsensical LMCache pool size) instead of an immediate, obvious error at the point of the typo.
Step-by-step proof of the regression mechanism (not a live failure today, since check_env_vars still validates required vars and the arithmetic guards catch $GPU_COUNT):
- Suppose a future edit misspells
$TOTAL_CPU_DRAM_GBas$TOTAL_CPU_DRAM_Ginside the lmcache branch'sLMCACHE_L1_SIZE_GB=$((TOTAL_CPU_DRAM_GB * 3 / 4))line. - Under
set -u(the previous/sibling-script behavior), bash would immediately abort withTOTAL_CPU_DRAM_G: unbound variable, pointing directly at the typo. - Under this PR's
set -eo pipefail(nounset disabled), the misspelled var silently expands to empty string, the arithmetic becomes$(( * 3 / 4 ))→ bash arithmetic treats an empty operand as0, soLMCACHE_L1_SIZE_GB=0. - The script proceeds to start
lmcache server --l1-size-gb 0 ..., which either fails deep inside the LMCache server startup logs (unclear connection to the root cause) or silently runs a degenerate 0-size cache — either way, a much harder failure to root-cause than an immediate bash error at the typo site.
Fix: Restore set -euo pipefail on line 2, and change the three unguarded references to use parameter expansion consistent with the B200 sibling: [[ -n "${SLURM_JOB_ID:-}" ]], ${SLURMD_NODENAME:-unknown}, [[ -n "${MODEL_PATH:-}" ]], and case "${KV_OFFLOAD_BACKEND:-}" in "").
On severity: All four verifiers independently confirmed the factual claim (only script in the directory without nounset, unnecessary given the ${VAR:-} idiom is used successfully in the sibling script) but converged on nit rather than normal, and I agree. check_env_vars still validates every genuinely required variable, GPU_COUNT is separately regex-validated, and the script runs correctly today as written on every current sweep point — the harm is a hypothetical future maintenance hazard, not a concrete failure in this PR. That's a real, worth-fixing consistency/robustness regression, but not a merge blocker.
|
see unofficial run visualizer at https://inferencex.semianalysis.com/inference?unofficialRun=29463061871 |
Description
Combines the tuned DeepSeek-V4 FP4 B300 vLLM AgentX recipe from #2225 with the LMCache KV-offload backend from #2153, mirroring the B200 arm in #2231, so the LMCache arm runs on the same image and serving flags as the vllm-simple and Mooncake arms and the backends are directly comparable.
benchmarks/single_node/agentic/dsv4_fp4_b300_vllm.shadopts the [AgentX] vLLM DeepSeek-V4 B300 aggregate / vLLM DeepSeek-V4 B300 聚合配置 #2225 tuned recipe verbatim (imagevllm/vllm-openai:nightly-dev-x86_64-cu13.0.1-904e4ec,FLASHINFER_MLA_SPARSE_DSV4attention,FULL_DECODE_ONLYCUDA graphs with every batch size captured, AMXF4 mega-MoE on EP tiers) and adds anlmcacheoffload-backend case: LMCache 0.5.1 MP server (lmcache_driventransfer mode) +LMCacheMPConnector, with the L1 pool derated to 75% ofTOTAL_CPU_DRAM_GB(per [LMCache] Add LMCache configs for dsv4 vllm b200/b300 agentix setups #2153 bring-up).PYTORCH_ALLOC_CONF=expandable_segments:Trueis not set, becauseLMCacheMPConnectorexports the KV cache to the LMCache server through legacy CUDA IPC handles and expandable-segment (cuMem/VMM) allocations cannot be exported that way (register_kv_cachesfails withcudaErrorInvalidValue— the same failure mode that made [LMCache] Add LMCache arm on tuned DSV4 FP4 B200 vLLM AgentX recipe #2231 drop--enable-cumem-allocatoron B200).configs/nvidia-master.yamlupdates the parentdsv4-fp4-b300-vllm-agenticto the [AgentX] vLLM DeepSeek-V4 B300 aggregate / vLLM DeepSeek-V4 B300 聚合配置 #2225 image/search space (required — the rewritten shared script's flags don't exist onv0.23.0) and adds a standalonedsv4-fp4-b300-vllm-agentic-lmcachesection whose points mirror the vllm-simple ladders: TP4-DEP4 conc [8, 16, 24, 32, 40, 48, 56, 64, 72]; TP8-DEP8 conc [32, 64, 96, 128, 160, 192, 224, 228].perf-changelog.yamlentry triggers onlydsv4-fp4-b300-vllm-agentic-lmcache; the parent points are benchmarked by [AgentX] vLLM DeepSeek-V4 B300 aggregate / vLLM DeepSeek-V4 B300 聚合配置 #2225 itself.Validated locally:
bash -npasses;python -m pytest utils/matrix_logic/ -q— 221 passed;generate_sweep_configs.py full-sweepemits all 17 lmcache points (router attached,total-cpu-dram-gbresolved fromdram-utilization: 0.80);validate_perf_changelog.pyagainstorigin/mainpasses. Not yet run on hardware — needs a sweep run. One thing to watch in bring-up: the B300 recipe runs--gpu-memory-utilization 0.96(vs 0.92 on B200), which could be tight with LMCache's GPU-side IPC mappings.中文说明
将 #2225 中调优后的 DeepSeek-V4 FP4 B300 vLLM AgentX 配方与 #2153 的 LMCache KV 卸载后端合并(对应 B200 的 #2231),使 LMCache 分支与 vllm-simple、Mooncake 分支在相同镜像和相同 serving 参数下运行,便于卸载后端间直接对比。
vllm/vllm-openai:nightly-dev-x86_64-cu13.0.1-904e4ec、FLASHINFER_MLA_SPARSE_DSV4注意力、逐批次捕获的FULL_DECODE_ONLYCUDA graph、EP 层级的 AMXF4 mega-MoE),并新增lmcache卸载后端分支:LMCache 0.5.1 MP server(lmcache_driven传输模式)+LMCacheMPConnector,L1 池按TOTAL_CPU_DRAM_GB的 75% 降额(沿用 [LMCache] Add LMCache configs for dsv4 vllm b200/b300 agentix setups #2153 调试结论)。PYTORCH_ALLOC_CONF=expandable_segments:True:LMCacheMPConnector通过传统 CUDA IPC 句柄将 KV cache 导出给 LMCache server,而 expandable segments 的 cuMem/VMM 分配无法以这种方式导出(register_kv_caches报cudaErrorInvalidValue,与 [LMCache] Add LMCache arm on tuned DSV4 FP4 B200 vLLM AgentX recipe #2231 在 B200 上去掉--enable-cumem-allocator的原因相同)。dsv4-fp4-b300-vllm-agentic更新为 [AgentX] vLLM DeepSeek-V4 B300 aggregate / vLLM DeepSeek-V4 B300 聚合配置 #2225 的镜像与搜索空间(必要改动——重写后的共享脚本参数在v0.23.0镜像上不存在),并新增独立的dsv4-fp4-b300-vllm-agentic-lmcache配置,测试点与 vllm-simple 阶梯对齐:TP4-DEP4 并发 [8, 16, 24, 32, 40, 48, 56, 64, 72];TP8-DEP8 并发 [32, 64, 96, 128, 160, 192, 224, 228]。perf-changelog.yaml条目仅触发dsv4-fp4-b300-vllm-agentic-lmcache;父配置的测试点由 [AgentX] vLLM DeepSeek-V4 B300 aggregate / vLLM DeepSeek-V4 B300 聚合配置 #2225 自身进行基准测试。本地验证:
bash -n通过;python -m pytest utils/matrix_logic/ -q— 221 项测试通过;generate_sweep_configs.py full-sweep生成全部 17 个 lmcache 测试点(带 router,total-cpu-dram-gb由dram-utilization: 0.80解析);validate_perf_changelog.py对比origin/main校验通过。尚未在硬件上运行——需要触发扫描。调试期间需关注:B300 配方使用--gpu-memory-utilization 0.96(B200 为 0.92),叠加 LMCache 的 GPU 侧 IPC 映射后余量可能偏紧。Related Issue
Builds on #2225 (tuned B300 AgentX recipe), #2153 (LMCache backend), and #2231 (the B200 counterpart of this PR). / 基于 #2225(B300 AgentX 调优配方)、#2153(LMCache 后端)与 #2231(本 PR 的 B200 对应版本)。
Type of Change
Checklist
perf-changelog.yamlperf-changelog.yamlentries are appended to the end of the file (the file is chronological: oldest at top, newest at bottom)OWNER/MEMBER/COLLABORATOR) has commented/reuse-sweep-runon this PR — do this only once there is a final full sweep that is all green with evals passing, since after this comment the sweep label will no longer automatically kick off new sweeps (remove and re-add the label to force one)🤖 Generated with Claude Code